Skip to content

Introduce policy-forwarding policy-counters#1418

Open
dplore wants to merge 17 commits intomasterfrom
dplore/user-counters
Open

Introduce policy-forwarding policy-counters#1418
dplore wants to merge 17 commits intomasterfrom
dplore/user-counters

Conversation

@dplore
Copy link
Copy Markdown
Member

@dplore dplore commented Dec 16, 2025

Change Scope

  • Introduce user-counters within the /network-instances/network-instance/policy-forwarding/policy subtree.
  • Add a policy-forwarding action to reference the per-policy user-counters
  • This change is backwards compatible.

Platform Implementations

Arista EOS and Juniper JunOS each have packet counter actions for their traffic-policy and filter based forwarding respectively.

Arista EOS traffic policies

traffic-policies
  traffic-policy send-v6-to-my_nhg
    match v6-dscp3 ipv6
         dscp 3
         !
         actions
            count my_nhg
            redirect next-hop group v6_my_nhg

JunOS filter based forwarding

      match my-af3-traffic ipv4
         source prefix field-set src-af3-prefixes
         destination prefix field-set dst-af3-prefixes
         actions
            count ip-af3-queue
            set dscp 24
            set traffic class 3
            redirect next-hop group ingress-vip
         !

See #1371 for additional implementation references and example operational use cases.

The CLI semantics are different in that action to perform a count and the user named counter are both created with a single line of CLI. In OC, we need a way to create the counter and stream the state of the counter. The user-defined counters container is added at the /network-instances/network-instance/policy-forwarding/policy subtree, allowing counters to be defined per policy. Each rule within the policy may define an action to count packets matching the rule.

Tree View

diff -wU 20 ~/old-tree.txt ~/pfcounter-tree.txt

         +--rw policy-forwarding
         |  +--rw config
         |  |  +--rw global-decap-policy?   string
         |  +--ro state
         |  |  +--ro global-decap-policy?   string
         |  +--rw policies
         |  |  +--rw policy* [policy-id]
         |  |     +--rw policy-id    -> ../config/policy-id
+        |  |     +--rw policy-counters
+        |  |     |  +--rw policy-counter* [name]
+        |  |     |     +--rw name      -> ../config/name
+        |  |     |     +--rw config
+        |  |     |     |  +--rw name?   string
+        |  |     |     +--ro state
+        |  |     |        +--ro name?       string
+        |  |     |        +--ro counters
+        |  |     |           +--ro packets?   oc-yang:counter64
+        |  |     |           +--ro bytes?     oc-yang:counter64
         |  |     +--rw config
         |  |     |  +--rw policy-id?   string
         |  |     |  +--rw type?        enumeration
         |  |     +--ro state
         |  |     |  +--ro policy-id?   string
         |  |     |  +--ro type?        enumeration
         |  |     +--rw rules
         |  |        +--rw rule* [sequence-id]
         |  |           +--rw sequence-id        -> ../config/sequence-id
         |  |           +--rw config
         |  |           |  +--rw sequence-id?   uint32
         |  |           +--ro state
         |  |           |  +--ro sequence-id?      uint32
         |  |           |  +--ro matched-pkts?     oc-yang:counter64
         |  |           |  +--ro matched-octets?   oc-yang:counter64
         |  |           +--rw l2
         |  |           |  +--rw config
         |  |           |  |  +--rw source-mac?             oc-yang:mac-address
         |  |           |  |  +--rw source-mac-mask?        oc-yang:mac-address
         |  |           |  |  +--rw destination-mac?        oc-yang:mac-address
@@ -3653,53 +3663,55 @@
         |  |           |  +--rw config
         |  |           |  |  +--rw source-port?                  oc-pkt-match-types:port-num-range
         |  |           |  |  +--rw source-port-set?              -> /oc-sets:defined-sets/port-sets/port-set/name
         |  |           |  |  +--rw destination-port?             oc-pkt-match-types:port-num-range
         |  |           |  |  +--rw destination-port-set?         -> /oc-sets:defined-sets/port-sets/port-set/name
         |  |           |  |  +--rw detail-mode?                  enumeration
         |  |           |  |  +--rw explicit-detail-match-mode?   enumeration
         |  |           |  |  +--rw explicit-tcp-flags*           identityref
         |  |           |  |  +--rw builtin-detail?               enumeration
         |  |           |  +--ro state
         |  |           |     +--ro source-port?                  oc-pkt-match-types:port-num-range
         |  |           |     +--ro source-port-set?              -> /oc-sets:defined-sets/port-sets/port-set/name
         |  |           |     +--ro destination-port?             oc-pkt-match-types:port-num-range
         |  |           |     +--ro destination-port-set?         -> /oc-sets:defined-sets/port-sets/port-set/name
         |  |           |     +--ro detail-mode?                  enumeration
         |  |           |     +--ro explicit-detail-match-mode?   enumeration
         |  |           |     +--ro explicit-tcp-flags*           identityref
         |  |           |     +--ro builtin-detail?               enumeration
         |  |           +--rw action
         |  |           |  +--rw config
+        |  |           |  |  +--rw count?                             -> ../../../../../policy-counters/policy-counter/config/name
         |  |           |  |  +--rw discard?                           boolean
         |  |           |  |  +--rw decapsulate-gre?                   boolean
         |  |           |  |  +--rw decap-network-instance?            -> /network-instances/network-instance/config/name
         |  |           |  |  +--rw decap-fallback-network-instance?   -> /network-instances/network-instance/config/name
         |  |           |  |  +--rw post-decap-network-instance?       -> /network-instances/network-instance/config/name
         |  |           |  |  +--rw network-instance?                  -> /network-instances/network-instance/config/name
         |  |           |  |  +--rw path-selection-group?              -> ../../../../../../../path-selection-groups/path-selection-group/config/group-id
         |  |           |  |  +--rw next-hop?                          oc-inet:ip-address
         |  |           |  |  +--rw next-hop-group?                    -> ../../../../../../../../static/next-hop-groups/next-hop-group/config/name
         |  |           |  |  +--rw decapsulate-mpls-in-udp?           boolean
         |  |           |  |  +--rw decapsulate-gue?                   boolean
         |  |           |  |  +--rw ip-ttl?                            uint8
         |  |           |  +--ro state
+        |  |           |  |  +--ro count?                             -> ../../../../../policy-counters/policy-counter/config/name
         |  |           |  |  +--ro discard?                           boolean
         |  |           |  |  +--ro decapsulate-gre?                   boolean
         |  |           |  |  +--ro decap-network-instance?            -> /network-instances/network-instance/config/name
         |  |           |  |  +--ro decap-fallback-network-instance?   -> /network-instances/network-instance/config/name
         |  |           |  |  +--ro post-decap-network-instance?       -> /network-instances/network-instance/config/name
         |  |           |  |  +--ro network-instance?                  -> /network-instances/network-instance/config/name
         |  |           |  |  +--ro path-selection-group?              -> ../../../../../../../path-selection-groups/path-selection-group/config/group-id
         |  |           |  |  +--ro next-hop?                          oc-inet:ip-address
         |  |           |  |  +--ro next-hop-group?                    -> ../../../../../../../../static/next-hop-groups/next-hop-group/config/name
         |  |           |  |  +--ro decapsulate-mpls-in-udp?           boolean
         |  |           |  |  +--ro decapsulate-gue?                   boolean
         |  |           |  |  +--ro ip-ttl?                            uint8
         |  |           |  +--rw encapsulate-gre
         |  |           |  |  +--rw config
         |  |           |  |  |  +--rw identifying-prefix?   oc-inet:ip-prefix
         |  |           |  |  +--ro state
         |  |           |  |  |  +--ro identifying-prefix?   oc-inet:ip-prefix
         |  |           |  |  +--rw targets
         |  |           |  |     +--rw target* [id]
         |  |           |  |        +--rw id        -> ../config/id

@OpenConfigBot
Copy link
Copy Markdown

OpenConfigBot commented Dec 16, 2025

No major YANG version changes in commit 1095666

@dplore dplore marked this pull request as ready for review December 17, 2025 00:21
@dplore dplore requested a review from a team as a code owner December 17, 2025 00:21
@earies
Copy link
Copy Markdown
Contributor

earies commented Jan 9, 2026

Regarding the proposal to introduce a generic list of top-level configurable user-counters - are there any existing implementation references to such a feature? The references provided do not indicate this.

I realize the desire here due to how such a named counter is referenced from other subtrees or accessed but speaking on behalf of JUNOS/EVO, this is not how this feature is implemented and named counters cannot be shared across references or domains and may also get derived names depending on if interface granularity is tagged.

As it stands in current implementation, this would result in phantom alien config nodes that have no real meaning and could give the perception of conformance for specific use on state only as I see it.

@amitarista
Copy link
Copy Markdown
Contributor

Arista EOS supports user-counters ( we call them named counters ), which are scoped strictly within individual policies. As shown in the below examples, these counters must be defined under a policy to take effect in accounting.

traffic-policies
   traffic-policy foo
      counter bar baz   <-- Define counter bar and baz under policy foo
      match rule1 ipv4
      actions
         count bar   <-- Using bar under rule1

The current proposal do not align with EOS implementation. By defining user-counters in the top-level tree, it implies they are global objects that can be shared across separate policies or other future entities that might use them.

@dplore
Copy link
Copy Markdown
Member Author

dplore commented Jan 23, 2026

Given the feedback, I'll restructure this to add named counters in-line with policy-forwarding policies.

I am thinking something like:

/network-instances/network-instance/policy-forwarding/policies/policy[name="foo"]/counters/counter[name="bar"]/matched-pkts  counter64
/network-instances/network-instance/policy-forwarding/policies/policy[name="foo"]/counters/counter[name="bar"]/matched-octets  counter64

@amitarista
Copy link
Copy Markdown
Contributor

Given the feedback, I'll restructure this to add named counters in-line with policy-forwarding policies.

I am thinking something like:

/network-instances/network-instance/policy-forwarding/policies/policy[name="foo"]/counters/counter[name="bar"]/matched-pkts  counter64
/network-instances/network-instance/policy-forwarding/policies/policy[name="foo"]/counters/counter[name="bar"]/matched-octets  counter64

These are counter-data paths. I’m assuming the entire user-counter* tree falls under the policy definition as well. Please clarify if that’s not the case.

@dplore
Copy link
Copy Markdown
Member Author

dplore commented Feb 4, 2026

Revised user-counters to be a container per policy and updated the PR description text and tree view. This still aligns with the operational use case and aligns better with referenced implementations which have "per policy" user defined counters.

In order to facilitate the creation of an 'anchor point' where the counter values can be subscribed to for streaming, this PR creates a user-counters container with config and state as well as a count action attaching the counter to a rule within a given policy-forwarding policy. This should be possible to map to native configuration examples where configuration of the counter occurs implicitly when a named counter is referenced in an 'action' statement.

@dplore
Copy link
Copy Markdown
Member Author

dplore commented Feb 4, 2026

@amitarista @earies @jsterne @rgwilton for your comments on the revised model

@dplore dplore moved this to Ready to discuss in OC Operator Review Feb 4, 2026
@earies
Copy link
Copy Markdown
Contributor

earies commented Feb 5, 2026

In JUNOS/EVO - there is no pre-definition of counter names that are referenced elsewhere.

But rather counter names are marked as inline actions. For the simplest use-cases, the proposed modeling here could be accepted w/ the predefined names as "phantom" config nodes that have no relevance that are then marked as actions. The state could be reflected in similar fashion.

e.g. For inline defined counter FOOt1

# run show firewall                

Filter: FOO                                                  
Counters:
Name                                                                            Bytes              Packets
FOOt1                                                                               0                    0

But the most common operational use-case is the desire for "interface-specific" counters which means the name of the counter that is defined inline is expanded to a different name based on the interface

e.g. For inline defined counter FOOt1 with interface-specific enabled

# run show firewall                                         

Filter: FOO-et-0/0/0.0-i                                     
Counters:
Name                                                                            Bytes              Packets
FOOt1-et-0/0/0.0-i                                                                  0                    0

The latter case cannot be covered in this scenario as counter names are derived and thus leafref validation would fail

@earies
Copy link
Copy Markdown
Contributor

earies commented Feb 5, 2026

To add to the comment on the OC Operators call 2026-02-05

This is proposing an action of count to this user-defined counter which conflicts with and duplicates the intent here:

module: openconfig-network-instance
  +--rw network-instances
     +--rw network-instance* [name]
        +--rw policy-forwarding
           +--rw policies
              +--rw policy* [policy-id]
                 +--rw rules
                    +--rw rule* [sequence-id]
                       +--ro state
                          +--ro sequence-id?      uint32
                          +--ro matched-pkts?     oc-yang:counter64
                          +--ro matched-octets?   oc-yang:counter64

With this proposal defining an opaque counter64 value with no unit of measurement.

It would be helpful to understand where these nodes fall short, if the intention is to deprecate and whether or not each rule should have an implicit counter enabled or not (for performance reasons)

@dplore
Copy link
Copy Markdown
Member Author

dplore commented Feb 10, 2026

I'll make changes to

  1. make the user-counters a state only list of "system defined" counters that match the names configured as action/config/count
  2. change the value leaf to matched-packets
  3. deprecate the existing counters:
     +--rw network-instance* [name]
        +--rw policy-forwarding
           +--rw policies
              +--rw policy* [policy-id]
                 +--rw rules
                    +--rw rule* [sequence-id]
                       +--ro state
                          +--ro sequence-id?      uint32
+                          x--ro matched-pkts?     oc-yang:counter64
+                          x--ro matched-octets?   oc-yang:counter64

@amitarista
Copy link
Copy Markdown
Contributor

I'll make changes to

  1. make the user-counters a state only list of "system defined" counters that match the names configured as action/config/count

EOS defines user-counters explicitly before they are used. config helps in tracking active or potential counters within a policy. While these could be deduced from actions/count as mentioned, explicit modeling provides a much more user-friendly experience

traffic-policies
   traffic-policy foo
      counter bar baz   <-- Define counter bar and baz under policy foo
      match rule1 ipv4
      actions
         count bar   <-- Using bar under rule1

@dplore
Copy link
Copy Markdown
Member Author

dplore commented Feb 17, 2026

I'll make changes to

  1. make the user-counters a state only list of "system defined" counters that match the names configured as action/config/count

EOS defines user-counters explicitly before they are used. config helps in tracking active or potential counters within a policy. While these could be deduced from actions/count as mentioned, explicit modeling provides a much more user-friendly experience

traffic-policies
   traffic-policy foo
      counter bar baz   <-- Define counter bar and baz under policy foo
      match rule1 ipv4
      actions
         count bar   <-- Using bar under rule1

Because there are (reconcilable) differences between implementations on this point, we will get operator consensus on the most operator friendly approach and choose a direction to go with the OC Model.

Scheduled for OC operator review on Feb 24, 2026

@dplore
Copy link
Copy Markdown
Member Author

dplore commented Feb 23, 2026

Among the referenced implementations, one native implementation requires a counter to be defined as an action without any other configuration and implicitly creates telemetry entities. In a second implementation, the counters are required to be explicitly declared and then used within policy actions.

To satisfy leafref requirements from a config node to a config node, it seems cleaner to have the policy-forwarding action count defined as a leafref to a config leaf in the tree of policy-counters.

@earies
Copy link
Copy Markdown
Contributor

earies commented Feb 23, 2026

To satisfy leafref requirements from a config node to a config node, it seems cleaner to have the policy-forwarding action count defined as a leafref to a config leaf in the tree of policy-counters.

If we step back from current implementations, as an operator - what does this pre-definition/reference buy anything other than the expense of additional nodes and bytes I'm curious?

There are other areas where it essentially means duplicates or referenced values must be in place thus meaning implementations must either be strict per YANG or relax such enforcements.

The opposite could also be that for an implementation that requires pre-definition and reference that it be handled by abstraction. No matter which direction, it poses complexity, true misalignment and potential corner cases ime but would rather go back to the operational benefits first.

(Having seen too many instances where payloads are not properly formed per similar expectations)

@amitarista
Copy link
Copy Markdown
Contributor

Among the referenced implementations, one native implementation requires a counter to be defined as an action without any other configuration and implicitly creates telemetry entities. In a second implementation, the counters are required to be explicitly declared and then used within policy actions.

To satisfy leafref requirements from a config node to a config node, it seems cleaner to have the policy-forwarding action count defined as a leafref to a config leaf in the tree of policy-counters.

IMHO defining counters via config is not only cleaner and consistent with our other models, but it also benefits operators by allowing a single counter to be referenced across multiple rules within a policy.

@earies
Copy link
Copy Markdown
Contributor

earies commented Feb 24, 2026

IMHO defining counters via config is not only cleaner and consistent with our other models, but it also benefits operators by allowing a single counter to be referenced across multiple rules within a policy.

That may not be how other implementations work today so modeling aside - is this driving an implementation behavior that is warranted to change or is essentially a new feature is the question.

Would like to hear from a few other implementations - @rgwilton @jsterne as well

@ElodinLaarz ElodinLaarz moved this from Ready to discuss to last-call in OC Operator Review Feb 24, 2026
@ElodinLaarz
Copy link
Copy Markdown
Contributor

Discussed at OC Operators Meeting on Feb 24, 2026:

Seems there's some ongoing discussion on the implementation details, though folks seem on board with the addition to the model.

Will allow folks to add input until next the community meet next week. (and then we'll merge at the next operators meet, if there are no objections.)

@jsterne
Copy link
Copy Markdown

jsterne commented Feb 24, 2026

I'm not certain I'm following all the back and forth, but it feels like a current implementation of a single vendor being proposed here, and there is questionable value as earies brings up (other than nice alignment for a particular implementation). I don't think it will be very realistic to map this to other implementations so it would really be driving a significant change and new functionality in many other vendors (especially 1:N relationships are expected, e.g. multiple arbitrary refs to the same counter).

@amitarista
Copy link
Copy Markdown
Contributor

IMHO defining counters via config is not only cleaner and consistent with our other models, but it also benefits operators by allowing a single counter to be referenced across multiple rules within a policy.

That may not be how other implementations work today so modeling aside - is this driving an implementation behavior that is warranted to change or is essentially a new feature is the question.

Would like to hear from a few other implementations - @rgwilton @jsterne as well

Btw just to be clear, I'm not referring the EOS config here. I was citing the overall config-state pattern in the OpenConfig modeling.

Reusability of the counter is applicable here since the same counter can be shared by multiple rules under a policy definition. Logical way to indicate this sharing is by making those rules point to the same counter entity via leafref.

@dplore
Copy link
Copy Markdown
Member Author

dplore commented Feb 25, 2026

In OC modeling we are looking for at least 2 examples of existing implementations for a feature (but not a particular native configuration style) or assertion from operator(s) that feature requests are in progress with multiple implementors.

We clearly have at least three pre-existing implementations of counters for policy-forwarding rules, noted below. This is not a vendor proprietary feature. The configuration schemas are indeed all different. Getting to a common schema is one of the key goals of OC and doing so means there's going to be some changes needed to adapt.

Comments should be directed regarding the functionality of what is being proposed. Secondarily, if we can make the single OC model easier for multiple implementations, that is a nice to have benefit.

Here are 4 references I've researched.

Arista EOS traffic policy counters

traffic-policies
   traffic-policy foo
      counter bar baz
      match rule1 ipv4
      actions
         count bar
      match rule2 ipv4
      actions
         count bar baz

JunOS filter based forwarding

Simplified example:

      match my-af3-traffic ipv4
         source prefix field-set src-af3-prefixes
         destination prefix field-set dst-af3-prefixes
         actions
            count ip-af3-queue
            set dscp 24
            set traffic class 3
            redirect next-hop group ingress-vip
         !

Cisco IOS XR implements ACL rules for packet matching in policy based forwarding. Those ACL rules can expose counters in the policybased routing and ACL based forwarding features (which are most similar to OC's "policy-forwarding" model here. So IOS XR does implement counters for matching rules, but the schema is rather different from other implementations and this model.

JunOS filter based forwarding

      match my-af3-traffic ipv4
         source prefix field-set src-af3-prefixes
         destination prefix field-set dst-af3-prefixes
         actions
            count ip-af3-queue
            set dscp 24
            set traffic class 3
            redirect next-hop group ingress-vip
         !

Nokia SR Linux
I am not able to find any equivalent Nokia SR Linux feature support for policy-forwarding counters (including the current OC defined counters) of any kind. This SR Linux doc documents support for policy-forwarding rules and actions for a recent release, but has no mention of counters.

@dplore dplore changed the title Introduce user-counters Introduce policy-forwarding policy-counters Feb 25, 2026
@dplore dplore requested a review from ElodinLaarz March 13, 2026 23:04
@ElodinLaarz
Copy link
Copy Markdown
Contributor

ElodinLaarz commented Mar 13, 2026

@dplore I think the failing CI checks are from openconfig/models-ci#112 ?? (Nvm: seems to have been failing before then...)

New thought is https://pkg.go.dev/golang.org/x/tools 0.43 came out and needs Go 1.25 .

@nokia1adam
Copy link
Copy Markdown
Contributor

On the debate about whether the counters should be predefined or added as state..

It's important to think about a use case where the same policy is applied to multiple interfaces. In this situation you could want one shared counter X to be incremented by packets matching the rule on all the applied interfaces or you could want per-interface, per-rule counters X1, X2, .. Xn. Both approaches can provide this choice, but they will do it differently:

With pre-configured counter names, the policy-counter itself needs a configuration property to indicate if it is to be used as a single shared counter, or a template for creating per-interface counters.

With auto-created counter names, the count X action in the policy-forwarding rule needs to say whether to use the shared counter with name X or whether to create a new interface-specific counter like X-ethernet1/1.

Both proposals end up in a situation where you have at least some (if not all) counters only being in state and not config.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: last-call

Development

Successfully merging this pull request may close these issues.

7 participants